#define _XOPEN_SOURCE  1
#define _XOPEN_SOURCE_EXTENDED 1
#include "utils.h"
#include "user.h"
#include "structure.h"

/**
 * @brief 删除文件
*/
void deleteFile(const char *file){
   unlink(file);
}



/**
 * @brief 判断是.还是..
*/
int isthisOrSuper(char *path){
	return strcmp(path, ".") == 0 || strcmp(path, "..") == 0;
}


/**
 * @brief 文件则直接删除，目录则递归删除目录下的所有文件和目录
*/
void deleteDirectoryRecursion(char *path){
   DIR *dirp = opendir(path);
   char pathname[255];
   struct dirent *direntP; 
   while((direntP = readdir(dirp)) != NULL){
      
      if(isthisOrSuper(direntP->d_name)){
         continue;
      }

      if(direntP->d_type != 4){
         // 拼接目录
         sprintf(pathname, "%s/%s", path, direntP->d_name);
         unlink(pathname);
      }else{
         // 拼接目录
         sprintf(pathname, "%s/%s", path, direntP->d_name);
         deleteDirectoryRecursion(pathname);
      }       
   }       
   rmdir(path);
}



/**
 * @return 目录返回1，文件返回0，无此文件返回-1
*/
int isExistThisFileOrDirectory(const char *path){
   struct stat st;
   memset(&st, 0, sizeof st);
   stat(path, &st);
   if(S_ISDIR(st.st_mode)){
      // 目录
      return 1;
   }else if(S_ISREG(st.st_mode)){
      // 文件
      return 0;
   }
   // 无此文件
   return -1;
}

/**
 * @brief 判断文件是否存在
 * @param filePath 文件路径
*/
int fileExists(const char *filePath){
   struct stat buffer;
   return (stat(filePath, &buffer) == 0);
}

/**
 * @brief 用来判断一个目录是否是另一个目录的上级目录，是返回1，不是返回0
 * @author yhx
 * @param parent_dir 你想要判断的父目录
 * @param child_dir  你想要判断的子目录
 * @return parent_dir 是 child_dir 的父目录则返回1，不是则返回0
*/
int isParentDirectory(const char *parent_dir, const char *child_dir) {
    char parent_abs[255] = {0};
    char child_abs[255] = {0};

    // 获取绝对路径
    realpath(parent_dir, parent_abs);
    realpath(child_dir, child_abs);

    // 排除 parent_dir 和 child_dir 相同的情况
    if (strcmp(parent_abs, child_abs) == 0) {
        return 0; // 不是父目录
    }

    // 检查 parent_dir 是否是 child_dir 的上级目录
    if (strstr(child_abs, parent_abs) == child_abs) {
        return 1;
    }

    return 0;
}


/**
 * @brief 用来拼接路径用，比如paramater(绝对路径) 为 /dir/ 则path返回 root/dir/
 * @param path 用来返回拼接后的结果，所以需要传入空数组
 * @param paramater 需要拼接的后续部分路径，比如 ../ 又或者 /dir/
*/
void stitchPaths(char *path,const char *paramater){
   // 如果是绝对路径则以root开头拼接
   if (paramater[0] == '/') {
      strcat(path,"root");
      strcat(path,paramater);
   }else {
      // 如果是相对路径则以user.currentDirectory开头拼接
      strcat(path, user.currentDirectory);
      strcat(path, "/");
      if (!strcmp(paramater,"ls")) {
      strcat(path, ".");
      }else {
      strcat(path, paramater);
      }
   }
}

/**
 * @brief 用来处理ls命令的处理逻辑
 * @return 成功返回0,不是目录失败返回1
*/
int dealListDirectoryContent(int net_fd,const char *paramater){
   // printf("utils.c dealListDirectoryContent paramater:%s\n",paramater);
   char path[255] = {0};
   // 拼接成实际的路径
   stitchPaths(path,paramater);
   // printf("utils.c dealListDirectoryContent 拼接后的路径为path: %s\n",path);
   DIR * dirp = opendir(path);
   // 打开目录流需要进行错误处理
   // ERROR_CHECK(dirp,NULL,"opendir");
   struct dirent * pdirent;
   char res[255] = {0};
   // 循环读目录项并拼接目录信息，循环结束的条件是返回值为NULL   
   while((pdirent = readdir(dirp)) != NULL){
      if (!strncmp(pdirent->d_name,".",1)) {
         continue;
        }
        strcat(res,pdirent->d_name);
        strcat(res,"\t");    // 字符类型打印
   }
   // printf("LS准备发送的内容:%s\n",res);
   // 拼接后先发送目录长度
   int len = strlen(res);
   send(net_fd,&len,sizeof(int),MSG_NOSIGNAL);
   // printf("dealLS.c 成功发送len: %d\n",len);
   // 如果len!=0,则发送结果字符串，如果为0，意味着没有任何内容，就不发了
   if (len != 0) {
      // printf("LS准备发送的内容:%s\n",res);
      send(net_fd,res,len,MSG_NOSIGNAL);
      // printf("dealLS.c 成功发送res: %s\n",res);
   // 及时释放资源，关闭目录流
   }else {
      printf("空目录，LS就不准备发了\n");
   }
   closedir(dirp);
   return 0;
}

/**
 * @brief 根据key从config文件中返回需要的value值
*/
int getValueByKeyFromConfig(char *key, char *value) {
   FILE *file = fopen("config/config.ini", "r");
	while (1) {
		char line[100];
      memset(line,0,sizeof(line));
		// 读一行数据
		char *res = fgets(line, sizeof(line), file);
		if (res == NULL) {
			char buf[] = "没有要找的内容 \n";
			memcpy(value, buf, strlen(buf));
			return -1;
		}
		// 处理数据
		char *line_key = strtok(line, "=");
		if (strcmp(key, line_key) == 0) {
			// 要找的内容
			char *line_value = strtok(NULL, "=");
			memcpy(value, line_value, strlen(line_value) - 1);
			return 0;
		}
	}

	return 0;
}


/**
 * @brief 从配置文件中获取ip
*/
char *getIpFromConfig(){
   char *ip = (char *)calloc(100,sizeof(char));
   getValueByKeyFromConfig("ip",ip);
   return ip;
}

/**
 * @brief 从配置文件中获取port
*/
char *getPortFromConfig() {
   char *port = (char *)calloc(100,sizeof(char));
   getValueByKeyFromConfig("port",port);
   return port;
}

/**
 * @brief 阻塞版recv，直到读到=length的字节数或对端断开，才解除阻塞
 * @param netFd 网络连接文件描述符
 * @param *ptr 指向一个内存指针，将读到的信息保存到该指针指向的内存
 * @param length 想要从缓冲区读的字节数
 * @return 返回读到的实际字节数，<=length，=0接收失败
 * @author moss
*/
off_t putFileRecv(int netFd, void *ptr, off_t lenght){
    off_t ret = recv(netFd, ptr, lenght, MSG_WAITALL);
    if(ret == 0){
        printf("客户端断开连接，接收失败\n");
    }
    return ret;
}

/**
 * @brief 判断工作路径下有没有同名文件
 * @param *paramater 指向需要判断的文件名的指针
 * @return 返回0，说明有同名文件，否则没有
*/
int errFileName(char *paramater){
    char *fileName = paramater;
    // 拼接路径
    char *pathStore = "/"; 
    char pathName[256] = {0}; 

    strcat(pathName, user.currentDirectory); 
    strcat(pathName, pathStore); 

    // 打开目录流
    DIR *pdir = opendir(pathName);

    // 遍历目录项，查看是否有同名文件
    struct dirent *pdirent; 
    while((pdirent = readdir(pdir)) != NULL){ 
        if(strcmp(fileName, pdirent->d_name) == 0){ 
             return 0;
        }
    }

    // 资源回收
    closedir(pdir);

    // 程序执行到此处，说明文件夹内没有同名文件
    return 1;
}

/**
 * @brief 客户端下载文件，通过函数进行发送文件
 *@param net_fd socket 返回的连接的文件描述符
 *@param params 要下载的文件名
*/
int sendFile(int net_fd,const char *parameter){
    //拼接路径
    char filePath[1024];
    snprintf(filePath,sizeof(filePath),"./%s/%s",user.currentDirectory,parameter);
    // 打开文件
    int file_fd = open(filePath, O_RDWR);  

    struct stat st;
    memset(&st, 0, sizeof st);
    fstat(file_fd, &st);
    send(net_fd, &st.st_size, sizeof(off_t), MSG_NOSIGNAL);

    // 写给客户端文件名字
    int name_len = strlen(parameter);
    // 先传文件名大小再传文件名
    // 别bb信号，免得我崩溃了😩
    send(net_fd, &name_len, sizeof(int), MSG_NOSIGNAL);
    send(net_fd, parameter, name_len, MSG_NOSIGNAL);

    // sendfile版本
    // 绕过缓冲区直接发到网卡
    sendfile(net_fd, file_fd, 0, st.st_size);

    
    close(file_fd);

    printf("File Successfully Sent\n");

    return 0;
}